/*Author: Rony Lahav (ID:043489889)*/

import java.util.HashMap;


public class UDPListenerProcessMessageSN extends UDPListenerProcessMessage implements Runnable{
	
	private boolean isFromSN = false;
	private boolean avFromSN = false;
	private boolean replySNLIST = false;
	
	public UDPListenerProcessMessageSN(String senderIP, int senderPort, String msg){
		super(senderIP,senderPort,msg);
		isFromSN = msg.startsWith("#");
		if (isFromSN)
			msg = msg.substring(1);
		avFromSN = msg.startsWith("#");	//2 "#" mean the SN has no peers and it sent its own AV message
		if (avFromSN)
			msg = msg.substring(1);
		replySNLIST = msg.startsWith("$");
		if (replySNLIST)
			msg = msg.substring(1);
		this.msg=msg;
	}
	
	public void run(){
		boolean ignore=false;
		boolean firstConnectedPeer=false;
		DBTuple senderTuple;
		if (!final_project.isShutDown()){
			if (isFromSN){
				if (msg.toUpperCase().startsWith(MessageCenter.TYPE_SNLIST)){ //SNLIST form another SN
					boolean sendReply = SNsHandler.getInstance().diagnoseSNList(msg,senderIP,senderPort,replySNLIST);
					if (sendReply){	//approve connection to SN by sending my own SNList
						String snList = "#" + MessageCenter.getInstance().createMessage_SNLIST_ForSNs(true);
						senderTuple = DataBase.getInstance().findSN(senderIP+":"+senderPort);
						sendMsgToOneTuple(senderTuple, snList);
					}
				}
				else{
					senderTuple = DataBase.getInstance().findSN(senderIP+":"+senderPort);
					if (senderTuple != null){	//unknown SNs that sent messages other than SNLIST are ignored	
						parsedMsg=MessageCenter.getInstance().parseValidMessage(msg, senderID, senderIP);
						//update last connection time of a peer and set its status to online
						senderTuple.setLastConnectionTime(System.currentTimeMillis());
						senderTuple.setStatus("1");
						senderID = parsedMsg.get(MessageCenter.VAR_ID);
						if (senderID==null)
							senderID=senderTuple.getPeerID();
						String msgType=parsedMsg.get("TYPE");
						DataBase.getInstance().updateSN(senderTuple);
						if (!final_project.isShutDown()){
							if (msgType.equals(MessageCenter.TYPE_IM)){
								SNsHandler.getInstance().propagateMessageToMyPeers(msg, senderID);
								SNsHandler.getInstance().propagateMessageToMySNs(msg, senderTuple.getKey());
							}
							else if (msgType.equals(MessageCenter.TYPE_FT)){
								long id = final_project.getNewUniqueID();
								//activate an outgoing TCP session for sending a GET message and receiving a file
								SingleTCPOutgoingConnectionSN tcpOutCon = new SingleTCPOutgoingConnectionSN(senderIP, Integer.parseInt(parsedMsg.get(MessageCenter.VAR_TCP)), parsedMsg.get(MessageCenter.VAR_FILE), Integer.parseInt(parsedMsg.get(MessageCenter.VAR_SIZE)), id, senderTuple.getKey(), senderID);
								Thread thread = new Thread(tcpOutCon);
								final_project.addThreadToLivingList(id, thread);
								thread.start();
							}
							else if (msgType.equals(MessageCenter.TYPE_AV)){
								//when SN has no peers it sends an AV to notify his still online and this shouldn't propagate
								if (!avFromSN){
									SNsHandler.getInstance().propagateMessageToMyPeers(msg, senderID);
									SNsHandler.getInstance().propagateMessageToMySNs(msg, senderTuple.getKey());
								}
							}
						}
					}
				}
			}
			else{
				senderTuple = DataBase.getInstance().findTuple(senderIP+":"+senderPort);
				if (senderTuple == null){	//new peer
					DBTuple newPeer = new DBTuple();
					newPeer.setPeerIP(senderIP);
					newPeer.setUdpPort(Integer.toString(senderPort));
					newPeer.setStatus("1");
					newPeer.setIsSN(false);
					newPeer.setLastConnectionTime(System.currentTimeMillis());
					DataBase.getInstance().insertTuple(newPeer);
					firstConnectedPeer = true;
					senderTuple = DataBase.getInstance().findTuple(senderIP+":"+senderPort);
				}
				parsedMsg=MessageCenter.getInstance().parseValidMessage(msg, senderID, senderIP);
				//update last connection time of a peer and set its status to online
				senderTuple.setLastConnectionTime(System.currentTimeMillis());
				senderTuple.setStatus("1");
				senderID = parsedMsg.get(MessageCenter.VAR_ID);
				if (senderID==null)
					senderID=senderTuple.getPeerID();
				//update ID of peer if it's the first time we get a message from it
				if (senderTuple.getPeerID()==null)
					senderTuple.setPeerID(senderID);
				String msgType=parsedMsg.get("TYPE");
				DataBase.getInstance().updateTuple(senderTuple);
				if (!final_project.isShutDown()){
					if (msgType.equals(MessageCenter.TYPE_IM)){
						SNsHandler.getInstance().propagateMessageToMyPeers(msg, senderID);
						SNsHandler.getInstance().propagateMessageToMySNs(msg, senderTuple.getKey());
					}
					else if (msgType.equals(MessageCenter.TYPE_FT)){
						long id = final_project.getNewUniqueID();
						//activate an outgoing TCP session for sending a GET message and receiving a file
						SingleTCPOutgoingConnectionSN tcpOutCon = new SingleTCPOutgoingConnectionSN(senderIP, Integer.parseInt(parsedMsg.get(MessageCenter.VAR_TCP)), parsedMsg.get(MessageCenter.VAR_FILE), Integer.parseInt(parsedMsg.get(MessageCenter.VAR_SIZE)), id, senderTuple.getKey(), senderID);
						Thread thread = new Thread(tcpOutCon);
						final_project.addThreadToLivingList(id, thread);
						thread.start();
					}
					else if (msgType.equals(MessageCenter.TYPE_AV)){
						if (firstConnectedPeer){	//AV from new peer => already added to list and set to online, now reply with SNList
							String snList = MessageCenter.getInstance().createMessage_SNLIST();
							sendMsgToOneTuple(senderTuple, snList);
						}
						SNsHandler.getInstance().propagateMessageToMyPeers(msg, senderID);
						SNsHandler.getInstance().propagateMessageToMySNs(msg, senderTuple.getKey());
					}
				}
			}
		}
	}
	
	protected String createAVMessage() {
		String message = MessageCenter.getInstance().createMessage_AV();
		return message;
	}
	
	protected void sendMsgToOneTuple(DBTuple tuple, String msg){
		// Open a dedicated thread to send FT to one specific peer
		SingleUDPMessageSender singleMsg = new SingleUDPMessageSender(final_project.mainUDPSoc, tuple, msg, final_project.getNewUniqueID());
		Thread thread = new Thread(singleMsg);
		final_project.addThreadToLivingList(singleMsg.getUniqueID(), thread);
		thread.start();
	}
}
